# This file is part of trytond-pcw-stock module for Tryton. 
# The COPYRIGHT file at the top level of this repository contains 
# the full copyright notices and license terms.
import math
from datetime import date, timedelta
from decimal import Decimal
from trytond.model import ModelSQL, ModelView, fields
from trytond.pyson import Eval, If, Bool, In
from trytond.transaction import Transaction
from trytond.pool import Pool, PoolMeta


__all__ = ['Template','Product']
__metaclass__ = PoolMeta

class Template:
    __name__ = "product.template"

    @classmethod
    def __setup__(cls):
        super(Template, cls).__setup__()
        cls._error_messages.update({
                'default_uom_change': "Default uom can not be changed for product '%s' because there is stock.move associated with it.",
                })

    @classmethod
    def write(cls, products, vals):
        Move = Pool().get('pcw.stock.move')
        Product = Pool().get('product.product')
        for pr in products: 
            if vals.get("default_uom"):
                product_product = Product.search([('template','=',pr.id)])
                for p in product_product:
                    moves = Move.search([('product','=',p.id)])
                    if moves:
                        cls.raise_user_error('default_uom_change', error_args=(str(pr.name),))                
        super(Template, cls).write(products, vals)


class Product:
    __name__ = "product.product"

    average_cost = fields.Property(fields.Numeric('Average Cost',
            digits=(16, 4), required=True))
    last_cost = fields.Property(fields.Numeric('Last cost',
            digits=(16, 4), required=True))
    cost_max = fields.Property(fields.Numeric('Max cost limit',
            digits=(16, 4)))
    cost_min = fields.Property(fields.Numeric('Min cost limit',
            digits=(16, 4)))
    quantity = fields.Function(fields.Float('Quantity Available', digits=(16, 4)), 'get_quantity')    
    uom_count = fields.Many2One('product.uom', 'UOM of count', 
            domain=[('category', '=', Eval('default_uom_category'))],
            depends=['active', 'default_uom_category'],
            help="UOM used when counting product")
    default_uom_category = fields.Function(fields.Many2One('product.uom.category', 
        'Default UOM Category'),'get_default_uom_category')


    def get_default_uom_category(self, name=None):
        return self.template.default_uom_category.id

    def get_quantity(self, name=None):
        return self.get_quantity_by_date()

    def get_quantity_by_date(self, date_start=None, date_end=None):
        if not date_start:
            date_start=date.min   
        if not date_end:
            date_end=date.max

        Snapshot = Pool().get('pcw.stock.snapshot')
        SnapshotLine = Pool().get('pcw.stock.snapshot.line')

        quantity = 0
        company = Transaction().context.get('company') 

        ss = Snapshot.search([('date','<=',date_end), ('company','=',company), ('state','=','done')], order=[('date','DESC')], limit=1)
        if ss:
            ss_prducts = SnapshotLine.search([('snapshot','=',ss[0].id), ('product','=',self.id)])
            if ss_prducts: 
                quantity = ss_prducts[0].quantity 
                if date_end == ss[0].date:
                    return quantity
                date_start =  ss[0].date + timedelta(days=1) 

        quantity_in = self.get_quantity_by_move('in', date_start, date_end)
        quantity_out = self.get_quantity_by_move('out', date_start, date_end)
        return quantity + (quantity_in - quantity_out)

    def get_quantity_by_move(self, in_out, date_start, date_end):
        quantity = 0
        cursor = Transaction().cursor
        company = Transaction().context.get('company') 

        sql_str = "SELECT SUM(internal_quantity) AS quantity FROM pcw_stock_move WHERE product = %s AND type = %s AND date >= %s AND date <= %s AND state = %s"

        if company:
            sql_str = sql_str + " AND company = %s" 
            cursor.execute (sql_str, [self.id, in_out, date_start, date_end, 'done', company])
        else:
            cursor.execute (sql_str, [self.id, in_out, date_start, date_end, 'done'])

        rows = Transaction().cursor.fetchall()
        for row in rows:
            qty = row[0]
            if qty: 
                quantity = quantity + qty

        return quantity

    def set_last_cost(self, new_cost, uom):
        if new_cost <= 0:
            return self.last_cost

        obj_uom = Pool().get('product.uom')
        cost = new_cost
        if self.default_uom.id != uom.id: 
            cost = obj_uom.compute_price(uom, new_cost, self.default_uom)
        cost = Decimal('%.4f' % cost)
        self.last_cost = cost
        self.save()
        return cost

    def set_average_cost(self, qty, new_cost, uom):
        if qty <= 0:
            return self.average_cost

        obj_uom = Pool().get('product.uom')

        cost_qty = 0;
        cost_price = 0;
        cost_qty = qty
        cost_price = new_cost

        if self.default_uom.id != uom.id: 
            cost_qty = obj_uom.compute_qty(uom, qty, self.default_uom)
            cost_price = obj_uom.compute_price(uom, new_cost, self.default_uom)
        cost_price = Decimal('%.4f' % cost_price)

        curr_qty = self.quantity
        curr_qty = curr_qty        

        if (curr_qty <= 0 and (cost_qty < math.fabs(curr_qty))):
            self.average_cost = cost_price
            self.save()
            return cost_price 

        if (curr_qty <= 0):
            total_cost_ant = 0
        else:
            total_cost_ant = Decimal(curr_qty) * self.average_cost

        average_price = (total_cost_ant + (Decimal(cost_qty) * cost_price)) / Decimal(curr_qty + cost_qty)
        average_price = Decimal('%.4f' % average_price)
         
        self.average_cost = average_price
        self.save()

        return average_price

    def get_quantity_by_uom(self, uom=None):
        if not uom:
            return self.quantity
        if self.default_uom.id == uom.id:
            return self.quantity

        obj_uom = Pool().get('product.uom')
        return obj_uom.compute_qty(self.default_uom, self.quantity, uom)

    def get_last_cost(self, uom=None):
        return self.get_last_cost_amount(self.last_cost, uom)

    def get_average_cost(self, uom=None):
        return self.get_last_cost_amount(self.average_cost, uom)

    def get_last_cost_amount(self, amount, uom=None):
        if not amount:
            return Decimal(0.0)
        if not uom:
            return amount 
        if self.default_uom.id == uom.id:
            return amount
        obj_uom = Pool().get('product.uom')
        return obj_uom.compute_price(self.default_uom, amount, uom)


